﻿using System.Net;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using Furion.Extensions;

namespace Vboot.Core.Module.Ass;

public class AssOssMainService : ITransient
{
    /// <summary>
    /// 上传文件
    /// </summary>
    /// <param name="file">文件</param>
    /// <returns></returns>
    public async Task<Zfile> UploadFile(IFormFile file)
    {
        MD5 md5Provider = MD5.Create();
        byte[] retVal = md5Provider.ComputeHash(file.ToByteArray());
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < retVal.Length; i++)
        {
            sb.Append(retVal[i].ToString("x2"));
        }

        var md5 = sb.ToString();


        var dbFile = await _repo.Context.Queryable<AssOssFile>()
            .Where(it => it.md5 == md5).FirstAsync();
        Zfile zfile = new Zfile();

        if (dbFile != null)
        {
            //相同文件已上传过
            AssOssMain main = new AssOssMain();
            main.id = YitIdHelper.NextId() + "";
            main.filid = dbFile.id;
            main.name = file.FileName;
            if (main.name.Contains("."))
            {
                main.type = main.name.Substring(main.name.LastIndexOf(".") + 1);
            }

            await _repo.InsertAsync(main);
            zfile.id = main.id;
            zfile.name = main.name;
            zfile.size = XfileUtil.GetFileSize(dbFile.size);
            zfile.path = dbFile.path;
            zfile.filid = dbFile.id;
        }
        else
        {
            AssOssFile newFile = null;
            //上传到本地
            if (_uploadOptions.Service == "local")
            {
                newFile = await LocalUpload(file, md5);
            }
            else if (_uploadOptions.Service == "aliyun")
            {
                newFile = AliyunUpload(file, md5);
            }

            await _repo.Context.Insertable(newFile).ExecuteCommandAsync();
            AssOssMain main = new AssOssMain();
            main.id = newFile.id;
            main.filid = newFile.id;
            main.name = file.FileName;
            if (main.name.Contains("."))
            {
                main.type = main.name.Substring(main.name.LastIndexOf(".") + 1);
            }

            main.crmid = XuserUtil.getUserId();
            await _repo.InsertAsync(main);
            zfile.id = main.id;
            zfile.name = main.name;
            zfile.size = XfileUtil.GetFileSize(newFile.size);
            zfile.path = newFile.path;
            zfile.filid = newFile.id;
        }

        return zfile;
    }

    public IActionResult DownloadFile(string table, string id)
    {
        string sql = "select t.path,t.name,f.service from " + table + " t " +
                     "inner join ass_oss_file f on f.id=t.filid " +
                     "where t.id=@id";
        dynamic map = _repo.Context.Ado.SqlQuerySingle<dynamic>(sql, new {id});
        if (map == null)
        {
            sql = "select f.path,t.name,f.service from ass_oss_main t " +
                  "inner join ass_oss_file f on f.id=t.filid " +
                  "where t.id=@id";
            map = _repo.Context.Ado.SqlQuerySingle<dynamic>(sql, new {id});
        }

        if ("aliyun" == map.service)
        {
            return AliyunDownload(map.name, map.path);
        }
        
        return LocalDownload(map.name, map.path);
    }


    private IActionResult LocalDownload(string name, string path)
    {
        var downloadPath = Path.Combine(App.WebHostEnvironment.WebRootPath, path);
        _httpContextAccessor.HttpContext.Response.Headers.Add("download-filename", name);
        return new FileStreamResult(new FileStream(downloadPath, FileMode.Open), "application/octet-stream")
            {FileDownloadName = name};
    }

    private IActionResult AliyunDownload(string name, string path)
    {
        _httpContextAccessor.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "download-filename");
        _httpContextAccessor.HttpContext.Response.Headers.Add("download-filename", HttpUtility.UrlEncode(name));
        return new FileStreamResult(AliyunOssUtil.GetObject(path).Content, "application/octet-stream")
            {FileDownloadName = name};
    }


    private async Task<AssOssFile> LocalUpload(IFormFile file, String md5)
    {
        string path = "upload/{yyyy}/{MM}/{dd}";
        var reg = new Regex(@"(\{.+?})");
        var match = reg.Matches(path);
        match.ToList().ForEach(a =>
        {
            var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2));
            path = path.Replace(a.ToString(), str);
        });

        // if (!_uploadOptions.ContentType.Contains(file.ContentType))
        //     throw Oops.Oh(ErrorCodeEnum.D8001);

        var sizeKb = (long) (file.Length / 1024.0); // 大小KB
        // if (sizeKb > _uploadOptions.MaxSize)
        //     throw Oops.Oh(ErrorCodeEnum.D8002);

        var suffix = Path.GetExtension(file.FileName).ToLower(); // 后缀
        if (suffix.Length > 0)
        {
            suffix = suffix.Substring(1);
        }

        // 先存库获取Id
        var id = YitIdHelper.NextId() + "";
        var finalName = id + "." + suffix; // 文件最终名称
        if (suffix == "")
        {
            finalName = id;
        }

        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, path);
        if (!Directory.Exists(filePath))
            Directory.CreateDirectory(filePath);
        await using var fs = File.Create(Path.Combine(filePath, finalName));
        await file.CopyToAsync(fs);
        var newFile = new AssOssFile
        {
            id = id,
            md5 = md5,
            size = file.Length,
            service = "local",
            path = path + "/" + finalName
        };
        return newFile;
    }

    private AssOssFile AliyunUpload(IFormFile file, String md5)
    {
        string path = "{yyyy}/{MM}/{dd}";
        var reg = new Regex(@"(\{.+?})");
        var match = reg.Matches(path);
        match.ToList().ForEach(a =>
        {
            var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2));
            path = path.Replace(a.ToString(), str);
        });

        // if (!_uploadOptions.ContentType.Contains(file.ContentType))
        //     throw Oops.Oh(ErrorCodeEnum.D8001);

        var sizeKb = (long) (file.Length / 1024.0); // 大小KB
        // if (sizeKb > _uploadOptions.MaxSize)
        //     throw Oops.Oh(ErrorCodeEnum.D8002);

        var suffix = Path.GetExtension(file.FileName).ToLower(); // 后缀
        if (suffix.Length > 0)
        {
            suffix = suffix.Substring(1);
        }

        // 先存库获取Id
        var id = YitIdHelper.NextId() + "";
        var finalName = id + "." + suffix; // 文件最终名称
        if (suffix == "")
        {
            finalName = id;
        }

        HttpStatusCode statusCode = AliyunOssUtil.PutObjectFromFile(file.OpenReadStream(), path + "/" + finalName, "");
        if (statusCode == HttpStatusCode.OK)
        {
            var newFile = new AssOssFile
            {
                id = id,
                md5 = md5,
                size = file.Length,
                service = "aliyun",
                path = path + "/" + finalName
            };
            return newFile;
        }
        else
        {
            return null;
        }
    }


    private readonly IHttpContextAccessor _httpContextAccessor;

    private readonly UploadOptions _uploadOptions;

    public SqlSugarRepository<AssOssMain> _repo { get; }

    public AssOssMainService(SqlSugarRepository<AssOssMain> repo,
        IHttpContextAccessor httpContextAccessor,
        IOptions<UploadOptions> uploadOptions)
    {
        _repo = repo;
        _uploadOptions = uploadOptions.Value;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<AssOssMain> SingleAsync(string id)
    {
        return await _repo.GetSingleAsync(t => t.id == id);
    }

    public async Task DeleteAsync(string[] ids)
    {
        await _repo.Context.Deleteable<AssOssMain>().In(ids).ExecuteCommandAsync();
    }
}